const express = require('express'); //express 4
const app = express();
const bodyParser = require('body-parser');
const multer = require('multer');
const upload = multer();
const cookieParser = require('cookie-parser')
const Session = require('express-session');
const Random = require("./Random");
const Goper = require("./GroupingOperator");

const APP_DOMAIN = "127.0.0.1";//<==自行修改
const APP_HOST = APP_DOMAIN + ":8080";
const APP_URL = "http://" + APP_HOST;
//自定义的质数集
const PRIMES = [
    [817504243, 817504253, 838041641, 838041647, 858599503, 858599509, 879190747, 879190841, 899809343, 899809363, 920419813, 920419823, 941083981, 941083987, 961748927, 961748941, 982451653, 982451707],
    [295075147, 295075153, 314606869, 314606891, 334214459, 334214467, 353868013, 353868019, 373587883, 373587911, 393342739, 393342743, 413158511, 413158523, 433024223, 433024253, 452930459, 452930477, 472882027, 472882049, 492876847, 492876863, 512927357, 512927377, 533000389, 533000401, 553105243, 553105253, 573259391, 573259433, 593441843, 593441861]
];
//算术操作符
const OPRTATORS = {
    "*": (a, b) => a * b,
    "-": (a, b) => a - b,
    "/": (a, b) => a / b,
    "+": (a, b) => a + b
};
//设置模板引擎为ejs
app.set('view engine', 'ejs'); //app = express() ; 
//设置模板文件位置 => 项目根路径下views目录为模板文件存放目录
app.set('views', __dirname + '/views');
app.use(cookieParser())
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(Session({
    name: "sid",
    secret: 'key',
    resave: true,
    rolling: true,
    saveUninitialized: false,
    cookie: { "maxAge": 60000 }
})); 

//随机计算符
function randomOperators() {
    return Random.numByArray(Object.keys(OPRTATORS));
}
//随机质数数组对象
function randomNumArr() {
    let obj = {
        _n: [Random.numByArray(PRIMES[0]), Random.numByArray(PRIMES[1])]
    };
    obj._r = [Goper.nums(obj._n[0]), Goper.nums(obj._n[1])];
    return obj
}
//随机质数数组对象与操作符
function randomNumOper() {
    let obj = randomNumArr();
    obj._o = randomOperators();
    return obj;
}

//全局拦截器
app.all('/*', function (req, res, next) {
    //排除
    for (let i of ["/505", '/cdn-cgi/l/chk_jschl',"/favicon.ico"]) if (req.url.indexOf(i) == 0) return next();
    //检查
    if (req.session.cf_clearance && req.session.cf_clearance == req.cookies.cf_clearance) {
        next();
    } else {
        req.session.cf_clearance = "";
        const a = randomNumArr();//获取初始化数据
        a._u = Random.num(3, 10);//计算次数
        a._v = Random.varName(10);//对象变量名
        a._t = Random.varName(Random.num(1, 5));//对象键变量名
        a._c = [];//计算数据
        a._h = a._n[0] / a._n[1];//当前值
        a._a = a._h;//计算结果
        for (let i = 0; i < a._u; i++) {//循环并计算
            let obj = randomNumOper();
            obj._h = obj._n[0] / obj._n[1];
            a._a = OPRTATORS[obj._o](a._a, obj._h);
            a._c.push(obj);
        }
        a._e = (+a._a + APP_HOST.length).toFixed(10);//用作对比的结果值
        a._l = `${a._v}={"${a._t}":${a._r.join("/")}};`;
        a._m = ";" + a._c.map(v => `${a._v}.${a._t}${v._o}=${v._r.join("/")};`).join("");
        let s = req.headers["user-agent"] + (+new Date()) + req.ip;//自定义的记录请求相关标识信息演示
        let s2 = Buffer.from(s).toString("hex");
        let data = {//记录相关数据
            headers: req.headers,
            js: a,
            form: {
                s: `${Random.md5(s)}-${parseInt((+new Date()) / 100)}-1800-${Buffer.from(s2).toString("base64")}`,
                jschl_vc: Random.uuidv4().split("-").join(""),
                pass: `${(+new Date()) / 100}-${Random.varName(10)}`,
            },
            host: APP_HOST,
            rayID: Random.uuidv1(s).split("-").join(""),
            url: req.url,
            cookie: { __cfduid: Random.uuidv4() }
        };
        req.session.renderData = data;//保存到会话中
        res.cookie('__cfduid', data.cookie.__cfduid, { domain: APP_DOMAIN, path: '/' });
        res.render('verification', data);//渲染页面
    }
});
//浏览器验证请求
app.get('/cdn-cgi/l/chk_jschl', function (req, res) {
    let s = req.param("s");
    let jschl_vc = req.param("jschl_vc");
    let pass = req.param("pass");
    let jschl_answer = req.param("jschl_answer");
    let __cfduid = req.cookies.__cfduid;
    let renderData = req.session.renderData;
    if (!renderData) return res.redirect('/');
    let url = renderData.url;
    if (__cfduid == renderData.cookie.__cfduid && s == renderData.form.s
        && jschl_vc == renderData.form.jschl_vc && pass == renderData.form.pass
        && jschl_answer == renderData.js._e && req.headers["user-agent"] == renderData.headers["user-agent"]) {
        //可以加上时间戳判断是否超过5秒
        req.session.cf_clearance = Random.uuidv1() + "-" + Random.uuidv4();
        res.cookie('cf_clearance', req.session.cf_clearance, { domain: APP_DOMAIN, path: '/' });
        delete req.session.renderData;
    }
    res.send(`<script>location.href="${url}";</script>`);
});
//假设是app的登陆页面
app.get('/', function (req, res) {
    let url = req.query.url;
    res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
    res.write(`<html> 
    <head>
    <meta charset="UTF-8">
    <title>Home Page</title>
    </head>
    <body> Home Page
    </body>
    </html>`);
    res.send();
});

//505
app.get('/505', function (req, res) {
    res.writeHead(505, { "Content-Type": "text/html; charset=utf-8" });
    res.write(`<html> 
    <head>
    <meta charset="UTF-8">
  <title>505</title>
  </head>
    <body> 505
    </body>
    </html>`);
    res.send();
});

app.listen(8080);